import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
import { CouresBgColorMap, CouresEnum, CouresLabelMap } from "@/enums/school/course.enum";
import { COURSE_NAMES, COURSES, WEEK_DAYS } from "../config";
import type { TableDataType, SubmitDataType, CardRefType, PropsType } from "../types/courseCard.type";

const CourseCard = forwardRef<CardRefType, PropsType>((props, ref) => {
  let currentSource: HTMLElement | null = null; // 当前目标源
  let currentTarget: HTMLElement | null = null; // 当前目标

  const divRef = useRef<HTMLDivElement>(null);
  const [tableData, setTableData] = useState<TableDataType[]>([]); // 表格数据

  // * 获取拖拽放手时节点
  const getDropNode = (node: HTMLElement) => {
    while (node) {
      if (node.dataset?.drop) return node;
      node = node.parentNode as HTMLElement;
    }
    return null; // 如果未找到具有 data-drop 属性的节点，则返回 null 或适当的默认值
  };

  // * 清空样式
  const clearStyles = () => {
    document.querySelectorAll(".active_drag").forEach(node => {
      node.classList.remove("bg-[#eee]", "active_drag");
    });
  };

  // * 处理拖拽事件
  const handleDrag = () => {
    // * 通过事件委派进行处理
    const DOM = divRef.current;
    if (!DOM) return;
    // * 拖拽开始
    DOM.ondragstart = (event: DragEvent) => {
      const target = event.target as HTMLElement;
      currentSource = target;
      // DataTransfer 对象用于保存拖动并放下（drag and drop）过程中的数据。这个对象可以从所有拖动事件 drag events 的 dataTransfer 属性上获取
      if (!event.dataTransfer) return;
      event.dataTransfer.effectAllowed = target.dataset["effect"] as DataTransfer["effectAllowed"]; // 自定义属性改变鼠标样式
      // 保存当前拖动元素的值
      event.dataTransfer.setData("value", target.dataset["value"]!);
      // 如果是表格移动到课程列表 则查询父节点 也就是 td 节点信息
      if (target.dataset["effect"] === "move") {
        currentTarget = target.parentNode as HTMLElement;
      }
    };
    // * 拖拽结束 在可拖动的元素或者被选择的文本被拖进一个有效的放置目标时（每几百毫秒）触发
    DOM.ondragover = (event: DragEvent) => {
      // 为确保 drop 事件始终按预期触发，应当在处理 dragover 事件的代码部分始终包含 preventDefault() 调用
      event.preventDefault();
    };
    // * 拖拽结束 和 ondragover 一样 ,只是 ondragenter 只触发一次
    DOM.ondragenter = (event: DragEvent) => {
      // 清空其他元素上的样式
      clearStyles();
      const dropNode = getDropNode(event.target as HTMLElement);
      // 没有节点表示整个区域不允许拖拽
      if (!dropNode || !currentSource) return;
      // 如果拖拽的元素和放置的元素 自定义属性的值相等 则添加激活样式
      if (currentSource.dataset["effect"] === dropNode.dataset["drop"]) {
        dropNode.classList.add("bg-[#eee]", "active_drag");
      }
    };
    // * drop 事件在元素或文本选择被放置到有效的放置目标上时触发
    DOM.ondrop = event => {
      const dropNode = getDropNode(event.target as HTMLElement);
      // 没有节点表示整个区域不允许拖拽
      if (!dropNode || !currentSource) return;
      if (currentSource.dataset["effect"] !== dropNode.dataset["drop"]) return;
      // 清空其他元素上的样式
      clearStyles();
      // 如果自定义属性 drop 的值为 copy
      if (dropNode.dataset["drop"] === "copy") {
        // 克隆拖拽元素
        const cloneNode = currentSource.cloneNode(true) as HTMLElement;
        cloneNode.dataset["effect"] = "move";
        // 把之前的元素内容清空
        dropNode.innerHTML = "";
        // 添加拖拽元素
        dropNode.appendChild(cloneNode);
        // 添加成功时的数据
        const sourceData = event.dataTransfer?.getData("value");
        const targetData = dropNode.dataset["value"];
        const tableValue: TableDataType = { courses: JSON.parse(sourceData!), weeks: JSON.parse(targetData!) };

        setTableData(prevData => {
          const existingItem = prevData.find(({ weeks }) => {
            // 放置在同一个位置上
            if (weeks.week === tableValue.weeks.week && weeks.index === tableValue.weeks.index) {
              return true;
            }
            return false;
          });
          if (existingItem) {
            // 说明是同样的课程放在同一个放在位置上
            if (existingItem.courses?.value === tableValue.courses?.value) {
              return prevData;
            } else {
              // 不同的课程放在一个位置上 懒得用id 直接字符串化对比对象（不建议）
              return [...prevData.filter(item => JSON.stringify(item) !== JSON.stringify(existingItem)), tableValue];
            }
          }
          return [...prevData, tableValue];
        });
      } else {
        // 否则则是表格拖拽到课程列表中
        // 直接删除节点
        currentSource?.remove();
        // 删除成功时的数据
        const sourceData = event.dataTransfer?.getData("value");
        const targetData = currentTarget?.dataset["value"];
        const tableValue: TableDataType = { courses: JSON.parse(sourceData!), weeks: JSON.parse(targetData!) };
        setTableData(prevData => {
          return prevData.filter(item => JSON.stringify(item) !== JSON.stringify(tableValue));
        });
      }
    };
  };

  // * 辅助函数 处理表格数据
  const handleTableData = (list: TableDataType[]) => {
    const newList: SubmitDataType[] = [];
    for (let i = 0, len = list.length; i < len; i++) {
      const item = list[i];
      // 将星期数据进行拼接
      newList.push({
        week: item.weeks.week,
        course: item.courses?.value as `${CouresEnum}`,
        index: item.weeks.index,
        node: item.node,
      });
    }
    // 按照第几节课进行排序
    return newList.sort((item1, item2) => item1.index - item2.index);
  };

  // * 保存
  const onSubmit = () => handleTableData(tableData);

  // * 处理回显数据
  const handleEchoData = () => {
    if (!props?.echoData?.length) return;
    const echoData = props.echoData;
    // 获取 tbody 下所有 td
    const tdNodes = document.querySelectorAll("tbody td") as NodeListOf<HTMLElement>;
    const nodeValueList = Array.from(tdNodes)
      .filter(node => node.dataset.value)
      .map(node => ({ weeks: JSON.parse(node.dataset.value!), node }));
    const nodeData = handleTableData(nodeValueList);
    setTableData(() => {
      return echoData.map(item => {
        return {
          weeks: {
            index: item.index,
            week: item.week,
          },
          courses: {
            bgColor: CouresBgColorMap[item.course],
            label: CouresLabelMap[item.course],
            value: item.course,
          },
        } as any;
      });
    });
    // 拿到每一天的数据
    outerLoop: for (let index = 0, length = nodeData.length; index < length; index++) {
      const nodeItem = nodeData[index];
      const div = document.createElement("div");
      if (nodeItem.node?.children.length) nodeItem.node.innerHTML = "";
      for (let i = 0, len = echoData.length; i < len; i++) {
        const weekItem = echoData[i];
        if (nodeItem.week !== weekItem.week) continue;
        if (nodeItem.index !== weekItem.index) continue;
        div.className = "h-40 leading-[40px] text-center text-white mt-4 first:mt-0";
        div.style.backgroundColor = CouresBgColorMap[weekItem.course];
        div.innerHTML = CouresLabelMap[weekItem.course];
        // 添加 div 属性
        div.setAttribute("draggable", "true");
        div.setAttribute("data-effect", "move");
        div.setAttribute("data-value", JSON.stringify(weekItem));
        nodeItem.node!.appendChild(div);
        continue outerLoop; // 匹配到后跳出对应命名循环
      }
    }
  };

  useImperativeHandle(ref, () => ({ onSubmit, handleEchoData }));

  useEffect(() => {
    handleEchoData();
    // eslint-disable-next-line
  }, [props.echoData]);

  useEffect(() => {
    handleDrag();
    // 修复 eslint 警告: React Hook useEffect has a missing dependency: 'handleDrag'. Either include it or remove the dependency array
    // eslint-disable-next-line
  }, [tableData]);
  return (
    /**
     * data-drop 元素支持拖拽的状态
     * data-effect 拖拽的时候元素鼠标样式
     */
    <div>
      {/*  */}
      <div className="flex items-center justify-center mt-24" ref={divRef}>
        {/* 课程名称列表 */}
        <div className="mr-24 w-100 h-full border border-[#ccc] border-solid p-4" data-drop="move">
          {COURSE_NAMES.map(item => {
            return (
              <div
                className="h-40 leading-[40px] text-center text-white mt-4 first:mt-0"
                style={{ backgroundColor: item.bgColor }}
                key={item.label}
                draggable
                data-effect="copy"
                data-value={JSON.stringify(item)}
              >
                {item.label}
              </div>
            );
          })}
        </div>
        {/* 课程表 */}
        <table className="border-collapse w-800 text-center select-none">
          <thead className="h-40">
            <tr>
              {/* 占位 */}
              <th className="border border-[#ccc] border-solid"></th>
              {WEEK_DAYS.map(item => {
                return (
                  <td className="border border-[#ccc] border-solid" key={item.value}>
                    {item.label}
                  </td>
                );
              })}
            </tr>
          </thead>
          <tbody>
            <tr className="h-43 box-border">
              <td className="border border-[#ccc] border-solid" rowSpan={4}>
                上午
              </td>
              {WEEK_DAYS.map(item => {
                return (
                  <td
                    className="border border-[#ccc] border-solid"
                    key={item.value}
                    data-drop="copy"
                    data-value={JSON.stringify({ week: item.value, index: 1 })}
                  ></td>
                );
              })}
            </tr>
            {Array.from({ length: COURSES - 1 }).map((_, index) => {
              return (
                <tr className="h-43 box-border" key={index}>
                  {WEEK_DAYS.map(item => {
                    return (
                      <td
                        className="border border-[#ccc] border-solid"
                        key={item.value}
                        data-drop="copy"
                        data-value={JSON.stringify({ week: item.value, index: index + 2 })}
                      ></td>
                    );
                  })}
                </tr>
              );
            })}
            {/* 占位 */}
            <tr className="h-16"></tr>
            {/*  */}
            <tr className="h-43 box-border">
              <td className="border border-[#ccc] border-solid" rowSpan={4}>
                下午
              </td>
              {WEEK_DAYS.map(item => {
                return (
                  <td
                    className="border border-[#ccc] border-solid"
                    key={item.value}
                    data-drop="copy"
                    data-value={JSON.stringify({ week: item.value, index: 5 })}
                  ></td>
                );
              })}
            </tr>
            {Array.from({ length: COURSES - 1 }).map((_, index) => {
              return (
                <tr className="h-43 box-border" key={index}>
                  {WEEK_DAYS.map(item => {
                    return (
                      <td
                        className="border border-[#ccc] border-solid"
                        key={item.value}
                        data-drop="copy"
                        data-value={JSON.stringify({ week: item.value, index: index + 6 })}
                      ></td>
                    );
                  })}
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
});
export default CourseCard;
